<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>响应式原理</title>
  </head>

  <body>
    <div id="app">
      <p>{{ num }}</p>
      <p>{{ num }}</p>
      <button @click="handleClick">按钮</button>
    </div>
    <script src="./vue.js"></script>
    <script>
      /*
      1. new Vue 做了什么事？
        beforeCreate
        initState -> 数据代理、数据劫持
        created
        $mount()
        beforeMount
        updateComponent = () => {
          vm._update(vm._render(), xxx);
        }
        new Watcher(vm, updateComponent, ...) -> new Watcher内部会调用updateComponent方法，到此页面完成渲染
        mounted

      2. 数据代理：将props、data、methods、computed数据代理到this上，可以通过this直接访问数据

        methods ->
          for (var key in methods) {
            vm[key] = methods[key].bind(vm)
          }
        
        props\data ->
          const keys = Object.keys(data)
          const i = keys.length;
          while(i--) {
            const key = keys[i];
            proxy(vm, '_data', key)
          }
        
          const proxy = (target, sourceKey, key) => {
            Object.defineProperty(target, key, {
              get() {
                return this[sourceKey][key]
              },
              set(val) {
                this[sourceKey][key] = val;
              }
            })
          }

      数据劫持：
        observe(data)
        new Observer(data)
        遍历data所有数据，通过defineReactive方法重新定义属性
          const keys = Object.keys(data) 
          for (var i = 0; i < keys.length; i++) {
            defineReactive(data, keys[i])
          }
        defineReactive
          const dep = new Dep() // 每一个data属性都会生成dep对象
          const val = data[key]
          observe(val) // 递归调用，确保data所有属性都要重新定义
          Object.defineProperty(data, key, {
            get() {
              if (Dep.target) {
                dep.depend() // 建立响应式联系（建立dep和watcher的联系）
              }
              return val;
            },
            set(newVal) {
              val = newVal; // 更新数据的值
              dep.notify() // 重新渲染页面，达到响应式
            }
          })


      3. 响应式：更新数据，数据变化，同时用户界面也会变化
    */
      debugger;
      new Vue({
        data() {
          return {
            num: 0,
          };
        },
        methods: {
          handleClick() {
            this.num++;
          },
        },
      }).$mount("#app");
    </script>
  </body>
</html>
